package com.example.exampleproject.base;

import android.util.Log;

import com.example.exampleproject.base.exception.BizError;
import com.example.exampleproject.base.exception.BusinessException;
import com.google.gson.Gson;
import com.google.gson.JsonIOException;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;

import java.io.Reader;

import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.ObservableTransformer;
import io.reactivex.functions.Function;
import okhttp3.ResponseBody;
import retrofit2.HttpException;

/**
 * 根据不同的业务error发射相应的异常,方便observer执行onError分发对应的事件
 *
 */
public class HandleBizErrTransformer<T> implements ObservableTransformer<T, T> {
    private static final String TAG = HandleBizErrTransformer.class.getSimpleName();

    //downstream,upstream
    @Override
    public ObservableSource<T> apply(Observable<T> upstream) {
//        调用onErrorResumeNext确保发生错误时会可以执行Observable.onError发射相应的错误事件类型
        return upstream.onErrorResumeNext(new BizErrorFunction<>());
    }

    private static class BizErrorFunction<T> implements Function<Throwable, Observable<T>> {
        @Override
        public Observable<T> apply(Throwable throwable) throws Exception {
            if (throwable instanceof HttpException) {
                HttpException httpException = (HttpException) throwable;
                ResponseBody responseBody = httpException.response().errorBody();
                //根据返回类型是否符合指定的异常类型发射对应的error事件
                if (responseBody != null) {
                    Gson gson = new Gson();
                    Reader reader = responseBody.charStream();
                    JsonReader jsonReader = gson.newJsonReader(reader);
                    try {
                        TypeAdapter<BizError> adapter = gson.getAdapter(TypeToken.get(BizError.class));
                        BizError result = adapter.read(jsonReader);
                        if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
                            return Observable.error(new JsonIOException("JSON document was not fully consumed."));
                        }
                        result.setHttpCode(httpException.code());
                        return Observable.error(new BusinessException(throwable, result));
                    } catch (Throwable t) {
                        Log.e(TAG, "parse json error:" + t.getMessage());
                        return Observable.error(t);
                    } finally {
                        responseBody.close();
                    }
                }
            }
            return Observable.error(throwable);
        }
    }
}
